package com.settlement.system.controller;

import com.alibaba.excel.context.AnalysisContext;
import com.settlement.system.common.enums.ChannelEnum;
import com.settlement.system.common.enums.UnificationStatusEunm;
import com.settlement.system.common.exception.ServiceException;
import com.settlement.system.common.util.common.DateUtil;
import com.settlement.system.common.util.common.FasUtil;
import com.settlement.system.common.util.common.RedisUtil;
import com.settlement.system.model.entity.common.*;
import com.settlement.system.reply.ObjectDataResponse;
import com.settlement.system.service.*;
import com.settlement.system.service.extend.BatchSave;
import io.swagger.annotations.Api;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 统一报表初始导入
 *
 * @author Ximi
 * @since 2020-06-18
 */

@Api(tags = "统一报表初始导入", description = "统一报表初始导入")
@Slf4j
@RestController
@RequestMapping("/unification/detail/initail")
public class TblUnificationBillDetailInitalController extends ExcelController<TblUnificationBillDetail> {

    public static final String BATCH_NUMBER = "acs_unification_detail_batch_number";

    @Resource
    private ITblReceiveRefundShopService tblReceiveRefundShopService;

    @Resource
    private ITblReceiveRefundAccountService tblReceiveRefundAccountService;

    @Resource
    private ITblOrderCloseAccountService orderCloseAccountService;

    @Resource
    private ITblRrRecordProjectMainService rrRecordProjectMainService;

    @Resource
    private ITblUnificationBillDetailService tblUnificationBillDetailService;

    @Resource
    private ITblReceiveRefundSupplierService tblReceiveRefundSupplierService;

    @Resource
    private ITblRrChannelService tblRrChannelService;

    @Resource
    private ITblRrBillTypeService tblRrBillTypeService;

    @Autowired


    @Override
    public IBaseService init() {
        return tblUnificationBillDetailService;
    }

    @Override
    public Class<?> getModelClass() {
        return TblUnificationBillDetail.class;
    }

    protected ThreadLocal<ConcurrentMap<String, List<TblUnificationBillDetail>>> orcSerialNoOriginDataListMap = ThreadLocal.withInitial(ConcurrentHashMap::new);

    /**
     * 验证导入的数据
     *
     * @param detail
     * @param analysisContext
     * @return
     */
    @Override
    public String verifyImportObject(TblUnificationBillDetail detail, AnalysisContext analysisContext) {

        if (analysisContext.readSheetHolder().getSheetNo() != 0) {
            return null;
        }

        if (StringUtils.isEmpty(detail.getShopNo())
                && StringUtils.isEmpty(detail.getShopAccount())) {
            return "零售店铺编码、店铺账号二者不能同时为空";
        }

        if (detail.getOutDate() == null) {
            return "日期不能为空";
        }

        String yyyyMM = DateUtil.format(detail.getOutDate(), "yyyyMM月");
        detail.setAccountingPeriod(yyyyMM);

        if (detail.getBillAmount() == null) {
            return "账单金额不能为空";
        }

        if (detail.getRecordAccountProject() == null) {
            return "记账项目不能为空";
        }

        if (detail.getOriginBillProject() == null) {
            return "原始账单项目不能为空";
        }

        if (StringUtils.isEmpty(detail.getBillType())) {
            return "账单类型不能为空";
        }

        detail.setStatus(UnificationStatusEunm.no_match.getNo());

        try {
            String billType = detail.getBillType();
            String billTypeNo = tblRrBillTypeService.getNoByName(billType);
            if (StringUtils.isEmpty(billTypeNo)) {
                return "[账单类型]不在【RR-账单类型】中，请确认后导入";
            }


            if ("线下打款-调账".equals(detail.getChannelName())) {
                if (StringUtils.isEmpty(detail.getSerialNo())) {
                    return "当渠道=线下打款-调账时,导入文件的交易流水号不能为空";
                }
                String serialNo = detail.getSerialNo();
                List<TblUnificationBillDetail> objects = orcSerialNoOriginDataListMap.get().get(serialNo);
                if (objects == null || objects.isEmpty()) {
                    Map<String, Object> params = new HashMap<>();
                    params.put("serialNo", serialNo);
                    params.put("channelNo", ChannelEnum.OR.getNo());
                    FasUtil.formatInQueryCondition(params, "serialNo", "channelNo");
                    objects = tblUnificationBillDetailService.selectByParam(params);
                }
                if (objects == null || objects.isEmpty() || objects.size() != 1 || StringUtils.isEmpty(objects.get(0).getThirdOrderNo())) {
                    if (objects == null || objects.isEmpty()) {
                        return "当渠道=线下打款-调账时，对应交易流水号，系统原数据不存在";
                    } else if (objects.size() != 1) {
                        return "当渠道=线下打款-调账时，对应交易流水号，系统原数据不能多于一行";
                    } else {
                        TblUnificationBillDetail tblUnificationBillDetail = objects.get(0);

                        if (!"收款-不能匹配到原始外部订单号".equals(tblUnificationBillDetail.getOriginBillProject())) {
                            return "当渠道=线下打款-调账时，对应交易流水号，系统原数据的原始账单项目应为“收款-不能匹配到原始外部订单号”";
                        }
                        if (detail.getBillAmount().abs().compareTo(tblUnificationBillDetail.getBillAmount().abs()) != 0) {
                            return "同一交易流水号，导入文件的账单金额绝对值与系统存在的原数据的账单金额绝对值应一致";
                        }

                        /*if(detail.getBillAmount().compareTo(tblUnificationBillDetail.getBillAmount().abs())==0){
                            if(StringUtils.isEmpty(detail.getThirdOrderNo())){
                                 //
                            }
                        }*/

                    }
                }
            }

            TblOrderCloseAccount closeAccount = null;
            if (!StringUtils.isEmpty(detail.getShopNo())) {
                TblReceiveRefundShop receiveRefundShop = tblReceiveRefundShopService.getByShopNo(detail.getShopNo());
                if (receiveRefundShop == null) {
                    return "店铺在店铺设置中不存在";
                }
                detail.setNcShopName(receiveRefundShop.getNcShopName());
                detail.setShopName(receiveRefundShop.getShopName());
                detail.setNcShopNo(receiveRefundShop.getNcShopNo());
                detail.setNcVirtualAccount(receiveRefundShop.getNcVirtualAccount());
                detail.setNcCustomerRecord(receiveRefundShop.getNcCustomerNo());

                detail.setCompanyNo(receiveRefundShop.getNcEnterAccountBody());
                if (StringUtils.isEmpty(detail.getChannelName())) {
                    detail.setChannelName(receiveRefundShop.getChannelName());
                }

                // FAS-12535 导入手工数据时，根据模板中的零售店铺查找出 入账主体，
                // 再根据入账主体+供应商名称，写入统一明细表时，
                // 查询供应商表，自动带出：供应商编码
                String supplierName = detail.getSupplierName();
                if (StringUtils.isNotEmpty(supplierName)) {
                    String ncEnterAccountBody = receiveRefundShop.getNcEnterAccountBody();
                    TblReceiveRefundSupplier supplier = tblReceiveRefundSupplierService.
                            getByAccountBookShortNameAndSupplierName(ncEnterAccountBody, supplierName);
                    if (supplier != null) {
                        detail.setNcSupplierRecord(supplier.getNcSupplierRecord());
                    }
                }

                closeAccount = orderCloseAccountService.getByThreeChannelNoAndShopAccount(receiveRefundShop.getThreeChannelNo(), null);
            }

            if (!StringUtils.isEmpty(detail.getShopAccount()) && StringUtils.isEmpty(detail.getShopNo())) {
                TblReceiveRefundAccount refundAccount = tblReceiveRefundAccountService.getOrByShopAccount(detail.getShopAccount());
                if (null == refundAccount) {
                    return "店铺账号在账号设置中不存在";
                }
                detail.setCompanyNo(refundAccount.getNcEnterAccountBody());
                if (StringUtils.isEmpty(detail.getChannelName())) {
                    detail.setChannelName(refundAccount.getChannelName());
                }
                if (StringUtils.isEmpty(detail.getNcVirtualAccount())) {
                    detail.setNcVirtualAccount(refundAccount.getNcVirtualAccount());
                }
                closeAccount = orderCloseAccountService.getByThreeChannelNoAndShopAccount(null, refundAccount.getShopAccount());
            }

            String noByName = tblRrChannelService.getNoByName(detail.getChannelName());
            if (StringUtils.isEmpty(noByName)) {
                return "渠道在渠道设置中不存在";
            }
            detail.setChannelNo(noByName);

         /*   if (null == closeAccount) {
                return "请设置订单稽核关账设置";
            }*/

            if (null != closeAccount && closeAccount.getRealCloseAccountDate().compareTo(detail.getOutDate()) >= 0) {
                return "已经关账不能导入";
            }

            if (CollectionUtils.isEmpty(rrRecordProjectMainService.getByRecordAccountProject(detail.getRecordAccountProject()))) {
                return "记账项目在记账项目设置中不存在";
            }

            if (StringUtils.isEmpty(rrRecordProjectMainService.getByChannelNoAndBillTypeAndOriginBillProject(detail.getChannelNo(), detail.getBillType(), detail.getOriginBillProject()))) {
                return "[渠道+账单类型+原始记账项目]在记账项目设置中未配置";
            }

            List<TblRrRecordProjectMain> types = rrRecordProjectMainService.getUnrepeatableVoucherTypes(detail.getRecordAccountProject(), detail.getChannelNo(), detail.getBillType());
            if (!types.isEmpty()) {
                String str = "";
                List<String> typeList = new ArrayList<>();
                for (TblRrRecordProjectMain type : types) {
                    if (StringUtils.isNotEmpty(type.getVoucherType()) && !str.contains(type.getVoucherType())) {
                        typeList.add(type.getVoucherType());
                    }
                }
                Set<String> set = sort(typeList);

                for (String type : set) {
                    str += (type + ",");
                }

                if (str.endsWith(",")) {
                    str = str.substring(0, str.length() - 1);
                }
                // [凭证类型]、[辅助项_收费项目] 、[稽核标识] 、[是否参与记账]
                detail.setVoucherType(str);
                detail.setIsJoinRecordAccount(types.get(0).getIsJoinRecordAccount());
                detail.setAuditMark(types.get(0).getAuditMark());
            } else {
                detail.setAuditMark("Y");
            }

            if (StringUtils.isNotBlank(detail.getRecordAccountProject())) {
                // NC_辅助项_收费项目
                String chargeProject = rrRecordProjectMainService.
                        getChargeProjectByRecordAccountProject(detail.getRecordAccountProject(), detail.getChannelNo());
                detail.setNcChargeProject(chargeProject);
            }
            TblRrBillType tblRrBillType = tblRrBillTypeService.getByName(billType);
            if (tblRrBillType != null) {
                detail.setBillCategory(tblRrBillType.getBillCategory());
            }
            detail.setBillType(billTypeNo);

        } catch (Exception ex) {
            log.error("system error:", ex);
            return "系统异常";
        }
        return null;
    }

    /**
     * 按照字符串的长度从小到大排序
     *
     * @param strs
     * @return
     */
    private static Set<String> sort(List<String> strs) {

        Set<String> set = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                int num = Integer.valueOf(s1.length()).compareTo(Integer.valueOf(s2.length()));
                //如果长度相等，则根据内容排序
                if (num == 0) {
                    return s1.compareTo(s2);
                }
                return num;
            }
        });

        for (String s : strs) {
            set.add(s);
        }

        return set;

    }

    synchronized long getNewBatchNumber() throws ServiceException {
        long redisBatchNumber = RedisUtil.incr(BATCH_NUMBER, 1);
        long batchNumber = redisBatchNumber;
        if (!checkBatchNumber(batchNumber)) {
            batchNumber = tblUnificationBillDetailService.selectTheNewestBatchNumber();
            batchNumber++;
            while (!checkBatchNumber(batchNumber)) {
                batchNumber++;
            }
            if (redisBatchNumber < batchNumber) {
                redisBatchNumber = RedisUtil.incr(BATCH_NUMBER, (batchNumber - redisBatchNumber));
            }
        }
        return redisBatchNumber;
    }

    boolean checkBatchNumber(long batchNumber) throws ServiceException {
        Integer integer = tblUnificationBillDetailService.selectOneByBatchNumber(batchNumber);
        return integer == 0;
    }

    /**
     * 保存数据
     *
     * @param datas
     * @throws ServiceException
     */
    @Override
    protected void saveData(List<TblUnificationBillDetail> datas, AnalysisContext analysisContext) throws ServiceException {


        if (analysisContext.readSheetHolder().getSheetNo() != 0) {
            return;
        }

        Date nullOutDateAkc = DateUtil.parseToDate("2000-01-01 00:00:00", "yyyy-MM-dd HH:mm:ss");

        List<String> orcSerialNoList = validateOrcSerialNo(datas, analysisContext);
        // todo:

        String error = validateSerialNo(datas, analysisContext);
        if (!StringUtils.isEmpty(error)) {
            throw new ServiceException(error);
        }

        long batchNumber = getNewBatchNumber();
        long stamp = System.currentTimeMillis();
        for (int index = 0; index < datas.size(); index++) {
            datas.get(index).setProblemDesc(0);
            datas.get(index).setOriginBillId("MK-" + batchNumber + "-" + stamp + "-" + index);
            datas.get(index).setUnificationBillId(datas.get(index).getOriginBillId());
            //爱库存统一账单为空，数据表存为2000年
            if (ChannelEnum.AKC.getNo().equals(datas.get(index).getChannelNo()) && datas.get(index).getOutDate() == null) {
                datas.get(index).setOutDate(nullOutDateAkc);
            }
        }

        try {
            new BatchSave(tblUnificationBillDetailService, datas).save();
            final String tip = "数据导入成功，批次号";
            if (StringUtils.isEmpty(message.get())) {
                message.set(tip + batchNumber);
            } else if (message.get().contains(tip)) {
                message.set(message.get() + "、" + batchNumber);
            }
            tblUnificationBillDetailService.updateOriginBillProjectBySerialNoList(orcSerialNoList);
        } catch (Exception ex) {
            tblUnificationBillDetailService.deleteByUnificationBillIds(datas.get(0).getUnificationBillId(),
                    datas.get(datas.size() - 1).getUnificationBillId());
            log.error("保存数据异常", ex);
            throw new ServiceException("保存数据异常", ex);
        } finally {
            orcSerialNoOriginDataListMap.get().clear();
        }
    }

    @Override
    @PostMapping("import")
    @CacheEvict(cacheNames = {
            "closeAccountByThreeChannelNoAndShopAccount", "rrRecordProject",
            "getByChannelNoAndBillTypeAndOriginBillProject",
            "rrOrAccountByLoginAccount", "rrShopByShopNo",
            "getUnrepeatableVoucherTypes", "getChargeProjectByRecordAccountProject",
            "getByAccountBookShortNameAndSupplierName"},
            allEntries = true, beforeInvocation = true)
    public ObjectDataResponse importFile(MultipartFile file, HttpServletRequest request) {
        try {
            ObjectDataResponse objectDataResponse = super.importFile(file, request);
            String s = message.get();
            //数据导入成功，批次号XXX、YYY
            log.info("返回批次号信息=" + s);
            if (StringUtils.isNotEmpty(s) && s.startsWith("数据导入成功，批次号")) {
                Map<String, Object> model = new HashMap<>();
                String batchNumbers = s.replace("数据导入成功，批次号", "")
                        .replace("、", ",");
                String[] split = batchNumbers.split(",");
                if (split != null && split.length > 0) {
                    List<String> batchNumberList = Arrays.asList(split);
                    model.put("batchNumberList", batchNumberList);
                }
                model.remove("batchNumber");
                tblUnificationBillDetailService.refreshShareOrNot(model);
                tblUnificationBillDetailService.refreshBeforeOrAfterTicket(model);
            }
            return objectDataResponse;
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);

            throw ex;
        }
    }

    private String validateOrcSerialNo2(List<TblUnificationBillDetail> datas) {
        BigDecimal orcBillAmount = BigDecimal.ZERO;
        List<TblUnificationBillDetail> list = new ArrayList<>();
        for (TblUnificationBillDetail data : datas) {
            if ("线下打款-调账".equals(data.getChannelName())) {
                list.add(data);
                orcBillAmount = orcBillAmount.add(data.getBillAmount());
            }
        }
        if (orcBillAmount.compareTo(BigDecimal.ZERO) != 0) {
            Map<String, BigDecimal> orcSerialNoBillAmountMap = new HashMap();
            for (TblUnificationBillDetail data : list) {
                if ("线下打款-调账".equals(data.getChannelName())) {

                }
                String serialNo = data.getSerialNo();
                BigDecimal bigDecimal = orcSerialNoBillAmountMap.get(serialNo);
                if (bigDecimal == null) {
                    orcSerialNoBillAmountMap.put(serialNo, data.getBillAmount());
                } else {
                    bigDecimal = bigDecimal.add(data.getBillAmount());
                    orcSerialNoBillAmountMap.put(serialNo, bigDecimal);
                }
            }
            Set<String> keySet = orcSerialNoBillAmountMap.keySet();
            for (String serialNo : keySet) {
                BigDecimal bigDecimal = orcSerialNoBillAmountMap.get(serialNo);
                if (bigDecimal.compareTo(BigDecimal.ZERO) != 0) {
                    return "当渠道=线下打款-调账时，导入文件中同一交易流水号，账单金额合计应为0";
                }
            }
        }
        return null;
    }

    /**
     * 验证流水
     *
     * @param datas
     * @return
     */
    private List<String> validateOrcSerialNo(List<TblUnificationBillDetail> datas, AnalysisContext analysisContext) throws ServiceException {

        if (analysisContext.readSheetHolder().getSheetNo() != 0) {
            return null;
        }

        List<String> orcSerialNoList = new ArrayList<>();

        HashMap<String, BigDecimal> map = new HashMap<>(16);
        for (TblUnificationBillDetail data : datas) {
            if ("线下打款-调账".equals(data.getChannelName())) {
                if (!StringUtils.isEmpty(data.getSerialNo())) {
                    if (!orcSerialNoList.contains(data.getSerialNo())) {
                        orcSerialNoList.add(data.getSerialNo());
                    }
                    if (map.get(data.getSerialNo()) == null) {
                        map.put(data.getSerialNo(), BigDecimal.ZERO);
                    }
                    BigDecimal bigDecimal = map.get(data.getSerialNo());
                    map.put(data.getSerialNo(), bigDecimal.add(data.getBillAmount()));
                }
            }
        }

        for (Map.Entry<String, BigDecimal> entry : map.entrySet()) {
            if (BigDecimal.ZERO.compareTo(entry.getValue()) != 0) {
                throw new ServiceException("当渠道=线下打款-调账时，导入文件中同一交易流水号，账单金额合计应为0;");
            }
        }

        return orcSerialNoList;
    }

    private String validateSerialNo(List<TblUnificationBillDetail> datas, AnalysisContext analysisContext) {

        if (analysisContext.readSheetHolder().getSheetNo() != 0) {
            return null;
        }

        HashMap<String, BigDecimal> map = new HashMap<>(16);
        HashMap<String, String> map2 = new HashMap<>(16);

        for (TblUnificationBillDetail detail : datas) {
            if (!StringUtils.isEmpty(detail.getSerialNo())) {
                if (map.get(detail.getSerialNo()) == null) {
                    map.put(detail.getSerialNo(), BigDecimal.ZERO);
                }
                BigDecimal bigDecimal = map.get(detail.getSerialNo());
                map.put(detail.getSerialNo(), bigDecimal.add(detail.getBillAmount()));

                if (map2.get(detail.getSerialNo()) == null) {
                    map2.put(detail.getSerialNo(), "");
                }

                if (StringUtils.isNotEmpty(detail.getThirdOrderNo())) {
                    map2.put(detail.getSerialNo(), detail.getThirdOrderNo());
                }
            }
        }

        for (Map.Entry<String, BigDecimal> entry : map.entrySet()) {
            if (BigDecimal.ZERO.compareTo(entry.getValue()) != 0) {
                return "导入的流水号" + entry.getKey() + "总和不等于0";
            }
        }

        for (Map.Entry<String, String> entry : map2.entrySet()) {
            if (StringUtils.isEmpty(entry.getValue())) {
                return "导入文件同一交易流水号" + entry.getKey() + "，外部订单号不能都为空";
            }
        }

        return null;
    }
}
